/*
 * 础石AUTOSAR CP操作系统 -- 平台总线
 *
 * Copyright (C) 2022 国科础石(重庆)软件有限公司
 *
 * 作者: Yongbo Zhang <zhangyongbo@kernelsoft.com>
 *
 * 基于linux/drivers/base/platform.c
 * Copyright (c) 2002-3 Patrick Mochel
 * Copyright (c) 2002-3 Open Source Development Labs
 *
 * License terms: GNU General Public License (GPL) version 3
 *
 */

#include <dim-sum/bus.h>
#include <dim-sum/devtree.h>
#include <dim-sum/errno.h>
#include <dim-sum/beehive.h>
#include <dim-sum/platform.h>
#include <dim-sum/delay.h>
#include "base.h"

static const struct dt_device_id *
dt_driver_match_device(struct device *dev, struct device_driver *drv)
{
	/*BUG_ON(!dev->dt_node || !drv->match_table);*/
	if (!dev->dt_node || !drv->match_table)
		return NULL;
	else if (!is_device_available(dev->dt_node))
		return NULL;

	return dt_match_node(drv->match_table, dev->dt_node);
}

static int platform_match(struct device *dev, struct device_driver *drv)
{
	BUG_ON(!dev || !drv);
	/**
	 * 优先通过设备树节点匹配
	 */
	if (dt_driver_match_device(dev, drv))
		return 1;

	return (strcmp(dev->name, drv->name) == 0);
}

static int platform_probe(struct device *dev)
{
	struct device_driver *drv = dev->driver;

	if (WARN_ON(!drv->probe))
		return -EINVAL;

	return drv->probe(dev);
}

static int platform_remove(struct device *dev)
{
	struct device_driver *drv = dev->driver;

	if (WARN_ON(!drv->remove))
		return -EINVAL;

	return drv->remove(dev);
}

static void platform_device_release(struct device *dev)
{
	dt_node_loosen(dev->dt_node);
	dev->dt_node = NULL;
	kfree(dev);
}

static struct bus_type platform_bus_type = {
	.name = "platform",
	.match = platform_match,
	.probe = platform_probe,
	.remove = platform_remove,
};
extern unsigned long devtree_virt_size;

static int __dt_platform_device_create(struct device_node *node, struct device *parent)
{
	struct device_node *child;
	struct device *dev;
	int ret;

	if (!node || !is_device_available(node)) {
		return -ENODEV;
	}
		

	dev = kzalloc(sizeof(struct device), PAF_ATOMIC);
	if (!dev)
		return -ENOMEM;

	device_initialize(dev);

	strlcpy(dev->name, node->name, sizeof(dev->name));

	dt_node_hold(node);
	dev->dt_node = node;
	dev->parent = parent;
	dev->bus = &platform_bus_type;
	dev->release = platform_device_release;
	
	ret = device_add(dev);
	if (ret)
		goto out_kfree_dev;

	/**
	 * 递归地创建子设备
	 */
	for_each_child_of_node(child, node) {
		ret = __dt_platform_device_create(child, dev);
		if (ret) {
			continue;
		}
	}

	return 0;

out_kfree_dev:
	kfree(dev);
	return ret;
}

int dt_platform_device_create(struct device_node *node)
{
	return __dt_platform_device_create(node, NULL);
}


static void platform_dt_emun_device(void)
{
	struct device_node *node, *root;
	void  parse_early_options(const char *cmdline);
	int ret;
	root = dt_find_node_by_path("/");
	if (!root)
		return;
	for_each_child_of_node(node, root) {
		if (!dt_find_property(node, "compatible"))
			continue;
		ret = dt_platform_device_create(node);
		if (ret) {
			dt_node_loosen(node);
		}
	}
	dt_node_loosen(root);
}

int platform_bus_init(void)
{
	bus_register(&platform_bus_type);
	platform_dt_emun_device();
	return 0;
}

int platform_driver_register_new(struct device_driver *drv)
{
	BUG_ON(!drv);

	drv->bus = &platform_bus_type;
	return driver_register(drv);
}

struct device *platform_device_find_by_dt_node(struct device_node *dt_node)
{
	struct device *found_dev;
	struct device *dev;

	find_dev_by_dt_node(dt_node, found_dev, dev, &platform_bus_type.dev_head, bus_list);
	return found_dev;
}